package com.framework.admin.service.impl;

import com.framework.admin.repository.MenuRepository;
import com.framework.admin.repository.SysRoleMenuRepository;
import com.framework.admin.enums.MenuTypeEnum;
import com.framework.admin.mapper.MenuMapper;
import com.framework.admin.model.SysMenu;
import com.framework.admin.service.IMenuService;
import com.framework.common.api.TreeOper;
import com.framework.common.constant.MenuConstant;
import com.framework.common.exception.BusinessException;
import com.framework.common.util.BeanUtils;
import com.framework.common.util.Constant;
import com.framework.common.util.StringUtils;
import com.framework.common.util.TreeUtils;
import com.framework.common.vo.TreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class MenuService implements IMenuService {
    @Autowired
    MenuRepository menuRepository;
    @Autowired
    MenuMapper menuMapper;
    @Autowired
    SysRoleMenuRepository sysRoleMenuRepository;

    @Override
    public String save(SysMenu menu) {
        SysMenu returnMenu;
        if (StringUtils.isBlank(menu.getPcode())) {
            menu.setPcode(MenuConstant.ROOT);
        }

        int exists = menuMapper.existsCode(menu);
        if (exists > 0) {
            throw new BusinessException("菜单编码重复");
        }

        MenuTypeEnum type = MenuTypeEnum.parse(menu.getType());

        if (!MenuTypeEnum.BUTTON.getValue().equals(menu.getType()) && StringUtils.isEmpty(menu.getId())) {
            //非创建按钮并且为新增时--获取
            if (StringUtils.isBlank(menu.getCode())) {
                Integer maxCodeNum = menuMapper.getMaxCodeNum();
                if (maxCodeNum == null) {
                    maxCodeNum = 1000001;
                }
                maxCodeNum++;
                menu.setCode(String.valueOf(maxCodeNum));
            }
        }

        checkPath(type, menu);

        //新增 校验路由节点是否重复
        if (StringUtils.isEmpty(menu.getId())) {
            int countPath = menuMapper.countPath(menu.getPcode(), menu.getPath());
            if (countPath != 0) {
                throw new BusinessException("路由节点重复,请重新输入!");
            }
        } else {
            //目前只级联一级修改子类 pcode
            SysMenu oldSysMenu = menuRepository.findOne(menu.getId());
            List<SysMenu> childs = menuMapper.getChildren(oldSysMenu.getCode());
            for (SysMenu child : childs) {
                child.setPcode(menu.getCode());
                menuRepository.save(child);
            }

            BeanUtils.copyProperties(menu, oldSysMenu);
            menu = oldSysMenu;
        }

        if (type == MenuTypeEnum.BUTTON) {
            SysMenu sysMenu = menuMapper.findByCode(menu.getCode());
            if (sysMenu == null) {
                // 新增
                returnMenu = menuRepository.save(menu);
            } else if (sysMenu.getId().equals(menu.getId())) {
                // 更新
                returnMenu = menuRepository.save(menu);
            } else {
                throw new BusinessException("按钮编码重复");
            }
        } else {
            returnMenu = menuRepository.save(menu);
        }

        return returnMenu.getId();
    }

    /**
     * 根据菜单编号，自动生成下级按钮
     * @param id 菜单编号
     */
    @Override
    public void createBtnMenu(String id) {
        SysMenu menu = menuRepository.findOne(id);
        String prefixCode = StringUtils.EMPTY;
        if (!Constant.ROOT.equals(menu.getCode())) {
            prefixCode = menu.getPcode();
        }
        prefixCode = prefixCode.concat(":").concat(menu.getCode());
        String prefixName = menu.getName();

        int count = 4;
        for (int i = 0; i < count; i++) {
            SysMenu btnMenu = BeanUtils.copyObject(menu, SysMenu.class, "id");
            btnMenu.setHidden(false);
            btnMenu.setPath(StringUtils.EMPTY);
            btnMenu.setComponent(StringUtils.EMPTY);
            btnMenu.setPcode(menu.getCode());
            if (i == 0) {
                btnMenu.setSort(1);
                btnMenu.setCode(prefixCode + ":add");
                btnMenu.setName(prefixName.concat("新增"));
            } else if (i == 1) {
                btnMenu.setSort(2);
                btnMenu.setCode(prefixCode + ":del");
                btnMenu.setName(prefixName.concat("删除"));
            } else if (i == 2) {
                btnMenu.setSort(3);
                btnMenu.setCode(prefixCode + ":edit");
                btnMenu.setName(prefixName.concat("编辑"));
            }

            btnMenu.setType(MenuTypeEnum.BUTTON.getValue());
            // 不存在相同code的新增， code不可重复
            if (menuMapper.findByCode(btnMenu.getCode()) == null) {
                menuRepository.save(btnMenu);
            }
        }
    }

    @Override
    public void delete(List<String> ids) {
        List<String> childrenIds = menuMapper.getChildrenIds(ids);
        int groupSize = 1000;
        int length = childrenIds.size();
        // 计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize;
        for (int i = 0; i < num; i++) {
            int toIndex = 1000;
            if (childrenIds.size() < groupSize) {
                toIndex = childrenIds.size();
            }
            List<String> subList = childrenIds.subList(0, toIndex);
            if (toIndex < childrenIds.size()) {
                childrenIds = childrenIds.subList(toIndex, childrenIds.size());
            }
            sysRoleMenuRepository.deleteByMenuIdIn(subList);
            menuRepository.deleteByIdIn(subList);
        }
    }

    @Override
    public Page<SysMenu> getMenusList(SysMenu menu, Pageable pageable) {
        return menuMapper.findMenus(menu, pageable);
    }

    @Override
    public List<TreeNode<SysMenu>> getMenuTree(String type) {
        List<SysMenu> list;
        Map<String, Object> params = new HashMap<>(16);
        list = menuMapper.findMenu(params);


        return TreeUtils.getTree(list, new TreeOper<SysMenu>() {
            @Override
            public String getItem(SysMenu o) {
                return o.getCode();
            }

            @Override
            public String getParent(SysMenu o) {
                return o.getPcode();
            }

            @Override
            public String getId(SysMenu o) {
                return o.getCode();
            }

            @Override
            public String getName(SysMenu o) {
                return o.getName();
            }

            @Override
            public List<SysMenu> getParentList(SysMenu it) {
                SysMenu param = new SysMenu();
                param.setCode(it.getPcode());
                return menuRepository.find(param);
            }
        });
    }

    @Override
    public SysMenu getByCode(SysMenu menu) {
        return menuRepository.findByCode(menu.getCode());
    }

    private void checkPath(MenuTypeEnum type, SysMenu menu) {
        switch (type) {
            case BUTTON:
                if (StringUtils.isEmpty(menu.getCode())) {
                    throw new BusinessException("请输入按钮编码");
                }
                if (menu.getPcode().equals(Constant.ROOT)) {
                    throw new BusinessException("请选择上级菜单");
                }
                break;
            case MENU:
                if (StringUtils.isEmpty(menu.getPath())) {
                    throw new BusinessException("请输入路由节点");
                }
                // 最上级必须以"/" 开头，否则出错
                if (MenuConstant.ROOT.equals(menu.getPcode())) {
                    if (!menu.getPath().startsWith("/")) {
                        menu.setPath("/".concat(menu.getPath()));
                    }
                } else {
                    //目录或菜单如果以“/”开始则截取
                    if (menu.getPath().startsWith("/")) {
                        menu.setPath(menu.getPath().substring(1));
                    }
                }
                break;
            case FOLDER:
                //导航栏菜单(一级菜单)如果未以“/”开始则添加前缀
                if (!menu.getPath().startsWith("/")) {
                    menu.setPath("/" + menu.getPath());
                }
                break;
            default:
                throw new BusinessException("菜单类型错误");
        }
    }
}
